package roborally.program.command;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Immutable;
import be.kuleuven.cs.som.annotate.Raw;

import roborally.itemsonboard.Robot;
import roborally.program.text.TextManipulation;

/**
 * A class representing a flow commands. These commands consists of several other commands.
 * 
 * @invar The FlowCommand must have valid internal commands.
 * 		| hasProperInteralCommands()
 * 
 * @author Molderez Tom, 2de bach Ingenieurswetenschappen Computerwetenschappen-Elektrotechniek and Reniers Jorn, 2de bach Ingenieurswetenschappen Werktuigkunde-Computerwetenschappen
 * @version 2.0
 *
 */
public abstract class FlowCommand extends Command {
	
	/**
	 * Creates a new FlowCommand with the given options.
	 * 
	 * @param commands A collection containing the internal commands of this flowCommand.
	 * @post For every command in commands, according the order, this new will contain the specific command.
	 * @throws IllegalArgumentException
	 * 		When the commands are not valid according isValidInternalCommands.
	 * 		| !isValidInternalCommands(commands)
	 */
	@Raw
	protected FlowCommand(List<? extends Command> commands) throws IllegalArgumentException {
		if(!isValidInteralCommands(commands))
			throw new IllegalArgumentException("The given collection of commands is not valid: " + commands.toString());
		this.internalCommands.addAll(commands);
	}
	
	/**
	 * Returns the internal commands of this in their particular order.
	 */
	@Basic @Immutable @Raw
	public List<Command> getInternalCommands() {
		return Collections.unmodifiableList(internalCommands);
	}
	
	/**
	 * Controls if the given List of commands is valid.
	 * 
	 * @param commands The List of commands to be controlled.
	 * @return True if every command in commands is different from null and is an instance
	 * of a flowcommand or an executionCommand. Otherwise false.
	 */
	@Raw
	public static boolean isValidInteralCommands(List<? extends Command> commands) {
		for(Command lusCommand : commands) {
			if(lusCommand == null)
				return false;
			else if(!FlowCommand.class.isInstance(lusCommand) && !ExecutionCommand.class.isInstance(lusCommand))
				return false;
		}
		return true;
	}
	
	/**
	 * Controls if the this has valid internalCommands.
	 * 
	 * @return True if every command in getInteralCommands() is different from null and is an instance
	 * of a flowcommand or an executionCommand. Otherwise false.
	 */
	@Raw
	public boolean hasProperInternalCommands() {
		for(Command lusCommand : getInternalCommands()) {
			if(lusCommand == null)
				return false;
			else if(!FlowCommand.class.isInstance(lusCommand) && !ExecutionCommand.class.isInstance(lusCommand))
				return false;
		}
		return true;
	}

	/**
	 * A List containing the internalCommands of this FlowCommand.
	 */
	private final List<Command> internalCommands = new ArrayList<Command>();
	
	/**
	 * Executes this FlowCommand by executing all the interal commands if the given
	 * robot is valid.
	 * 
	 * @param robot The robot that must be manipulated.
	 * @effect Every command in getInteralCommands() will be executed, in the specified order.
	 * @throws IllegalArgumentException
	 * 		When one or more internal commands can not be executed with the given robot as
	 * 		argument.
	 */
	@Override
	public void execute(Robot robot) throws IllegalArgumentException {
		for(Command lusCommand : getInternalCommands())
			lusCommand.execute(robot);
	}
	
	/**
	 * Search for and executes the (minimumSteps + 1) th basic command (execution command)
	 * The currentSteps is a counter that counts how many basic commands we already passed.
	 * When currentStep is equal to minimumSteps, that basic command will be executed.
	 * 
	 * @param minimumSteps The amount of basic commands we already have executed. 
	 * @param currentSteps The amount of basic commands we already passed.
	 * @param robot The robot that will be manipulated by the given program.
	 * @effect For each interalCommand, we will invoke the next method again. But when 
	 * currentSteps become greater then minimumSteps, no more must happen and the method
	 * jumps out of the loop.
	 * @return The new currentSteps which will increase when a basic command is found.
	 * @throws IllegalArgumentException
	 * 		When the robot is not valid.
	 * @note The return value is not always equal to the amount of basic commands of this command
	 * added by the start value of it. This is only the case if the result is smaller then
	 * minimumSteps. Otherwise it doesn't care because the stop condition of the recursive
	 * structure of the methods is satisfied.
	 */
	@Override
	public int next(final int minimumSteps, int currentSteps, Robot robot) throws IllegalArgumentException {
		for(Command lusCommand : getInternalCommands()) {
			currentSteps = lusCommand.next(minimumSteps, currentSteps, robot);
			if(currentSteps > minimumSteps)
				break;
		}
		return currentSteps;
	}
	
	/**
	 * Writes this FlowCommand nicely to text. 
	 * 
	 * @param leftSpaces The amount of left spaces for each command on the line.
	 * @return Each command in getInternalCommands() 
	 * is been writing on a single line in the right other with the given leftSpaces
	 * as spaces to the left. This result is returned in the form of a String.
	 */
	@Override
	public String write(String leftSpaces) {
		String write = new String();
		for(Command command : this.getInternalCommands())
			write = write + command.write(leftSpaces + TextManipulation.LEFT_SPACE);
		return write;
	}
}
